package com.pkk.spring.cloud.service.gateway.filter.global;

import static com.pkk.spring.cloud.service.gateway.mvc.code.GatewayCode.WHITE_LIST_LIMIT;

import cn.hutool.json.JSONUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.pkk.spring.cloud.config.nacos.properties.NacosProperties.NacosConfigProperties;
import com.pkk.spring.cloud.service.gateway.config.GatewayNacosConfigListener;
import com.pkk.spring.cloud.service.gateway.filter.SimpleGlobalFilter;
import com.pkk.spring.cloud.service.gateway.support.InMemoryUnionRepository;
import com.pkk.spring.cloud.service.gateway.util.IpUtil;
import com.pkk.spring.cloud.service.rpc.result.R;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.Executor;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * 白名单过滤器
 *
 * @author peikunkun
 * @version V1.0
 * @date 2021-01-26 14:12
 **/
public class GlobalWhitelistFilter extends InMemoryUnionRepository<String> implements SimpleGlobalFilter {


  @Autowired
  private GatewayNacosConfigListener gatewayNacosConfigListener;

  /**
   * 白名单配置持久化key
   */
  private static final String DEFAULT_WHITELIST_CONFIG_REPOSITORY_KEY = "white";


  /**
   * 初始化动态路由
   *
   * @author peikunkun
   * @date 2021/1/23 0023 上午 10:43
   */
  @Override
  public void init() throws Exception {
    final String configWithNode = gatewayNacosConfigListener
        .getConfigWithNode(DEFAULT_WHITELIST_CONFIG_REPOSITORY_KEY, DEFAULT_WHITELIST_CONFIG_REPOSITORY_KEY);
    if (StringUtils.isBlank(configWithNode)) {
      return;
    }
    super.add(JSONUtil.toList(configWithNode, String.class));

    final ConfigService config = gatewayNacosConfigListener
        .getConfig(gatewayNacosConfigListener.getConfigId(DEFAULT_WHITELIST_CONFIG_REPOSITORY_KEY));

    final NacosConfigProperties configWithConfigName = gatewayNacosConfigListener
        .getConfigWithConfigName(DEFAULT_WHITELIST_CONFIG_REPOSITORY_KEY);

    //初始化配置
    config.addListener(configWithConfigName.getDataId(), configWithConfigName.getGroup(), new Listener() {
      @Override
      public Executor getExecutor() {
        return null;
      }

      @SneakyThrows
      @Override
      public void receiveConfigInfo(String configInfo) {
        String config = gatewayNacosConfigListener.getConfig(configInfo, DEFAULT_WHITELIST_CONFIG_REPOSITORY_KEY);
        if (StringUtils.isBlank(config)) {
          config = "[]";
        }
        update(JSONUtil.toList(config, String.class));
      }
    });
  }

  /**
   * 更新数据
   *
   * @return void
   * @Param uniqued
   * @author peikunkun
   * @date 2021/1/27 0027 下午 4:15
   * @since
   */
  private void update(List<String> uniqued) {
    BLACK_LIST_CONTEXT.clear();
    this.add(uniqued);
  }


  /**
   * Process the Web request and (optionally) delegate to the next {@code WebFilter}
   * through the given {@link GatewayFilterChain}.
   *
   * @param exchange the current server exchange
   * @param chain provides a way to delegate to the next filter
   * @return {@code Mono<Void>} to indicate when request processing is complete
   */
  @SneakyThrows
  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    exchange.getResponse().setStatusCode(HttpStatus.OK);

    //获取不到或者主机是存在黑名单里的直接过滤
    final String ip = IpUtil.getIp(exchange.getRequest());
    if (StringUtils.isNotBlank(ip) && super.contains(ip)) {
      //否则放行
      return chain.filter(exchange);

    }

    //中文乱码
    final ServerHttpResponse response = exchange.getResponse();
    response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);

    DataBuffer dataBuffer = response.bufferFactory()
        .wrap(JSONUtil.toJsonStr(R.error(WHITE_LIST_LIMIT)).getBytes(StandardCharsets.UTF_8));
    return exchange.getResponse().writeWith(Flux.just(dataBuffer));
  }


  /**
   * Get the order value of this object.
   * <p>Higher values are interpreted as lower priority. As a consequence,
   * the object with the lowest value has the highest priority (somewhat
   * analogous to Servlet {@code load-on-startup} values).
   * <p>Same order values will result in arbitrary sort positions for the
   * affected objects.
   *
   * @return the order value
   * @see #HIGHEST_PRECEDENCE
   * @see #LOWEST_PRECEDENCE
   * @see #
   */
  @Override
  public int getOrder() {
    return 0;
  }
}
